package com.hdp.kotlin_samples.high

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import androidx.lifecycle.*
import com.hdp.kotlin_samples.R
import kotlinx.coroutines.*

/**
 * @Author: NYC
 * @CreateDate: 2021/3/29 4:02 PM
 * @Description:
 * kotlin中协程的使用
 *
 * 协程算是Kotlin一个非常先进的特性：
 * 让我们得以已同步的方式写出异步的代码，避免陷入Java的回调地狱问题
 *
 *
 * 这里是部分示例代码，对应解析：
 *
 * https://dashen.zhuanspirit.com/pages/viewpage.action?pageId=96338902
 *
 *
 */
class CoroutinesActivity : AppCompatActivity() {

    val mTvContent: TextView by lazy {
        findViewById(R.id.tv_content)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_coroutine)
        val content=findViewById<TextView>(R.id.tv_content)
        content.text = "是房东啥都放假哦"
        test()
        addListener()
    }

    private fun addListener() {
        findViewById<TextView>(R.id.tv_go_next).setOnClickListener {
            startActivity(Intent(this, CoroutinesActivity2::class.java))
            finish()
        }
    }

    private fun test() {
        useGlobalScope()
//        useCoroutineScope()
//        useAsyncAwait()
//        useAsyncAwait2()
//        useWithViewModel()
//        useWithLifeCycle()
//        useWithLiveData()
    }


    /**
     * 使用GlobalScope启动协程
     */
    private fun useGlobalScope() {
        showThreadName("1")
        GlobalScope.launch(Dispatchers.IO) { //在IO线程开始
            showThreadName("2")
            //IO 线程里拉取数据
            val result = fetchData()
            showThreadName("3")
            //主线程里更新 UI
            withContext(Dispatchers.Main) { //执行结束后，自动切换到UI线程
                showThreadName("4")
                mTvContent.text = result
            }
        }
        Log.e("TAG", "5" + Thread.currentThread().name)
    }

    /**
     * 使用自定义的CoroutineScope
     */
    private fun useCoroutineScope() {
        val userScope = Job()  //用来取消协程
        val uiScope = CoroutineScope(Dispatchers.Main + userScope)
        showThreadName("1")
        uiScope.launch {
            showThreadName("2")
            val result = withContext(Dispatchers.IO) {
                fetchData()
            }
            showThreadName("3")
            mTvContent.text = result
        }
        showThreadName("4")
    }

    /**
     * Async,Await
     * async 返回Deferred,可以理解为带返回值的launch函数
     */
    private fun useAsyncAwait() {
        GlobalScope.launch(Dispatchers.Main) {
            showThreadName("1")
            val ioScope = CoroutineScope(Dispatchers.IO)
            val result = ioScope.async { fetchData() }
            showThreadName("2")
            val showStr = result.await()
            showThreadName("3")
            mTvContent.text = showStr
        }
        showThreadName("4")
    }

    /**
     * Async,Await 组合多个异步操作结果
     */
    private fun useAsyncAwait2() {
        GlobalScope.launch(Dispatchers.Main) {
            showThreadName("1")
            val ioScope = CoroutineScope(Dispatchers.IO)
            val result1 = ioScope.async { fetchData() }
            val result2 = ioScope.async { fetchData2() }
            showThreadName("2")
            val showStr1 = result1.await()
            showThreadName("3")
            val showStr2 = result2.await()
            showThreadName("4")
            mTvContent.text = showStr1 + showStr2
        }
        showThreadName("5")
    }

    /**
     * 协程和viewmodel的使用
     * implementation  'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-alpha02'
     */
    private fun useWithViewModel() {
        //viewmodel清楚的时候，协程被cancel
        val viewmodel = ViewModelProvider(this).get(MyViewModel::class.java)
        viewmodel.viewModelScope.launch(Dispatchers.IO) {
            val result = fetchData()
            withContext(Dispatchers.Main) {
                showThreadName("1")
                mTvContent.text = result
            }
        }
        showThreadName("2")
    }


    /**
     * 协程在Activity和Fragment中结合LifeCycle的使用
     * implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha02'
     */
    private fun useWithLifeCycle() {
        lifecycle.coroutineScope.launch(Dispatchers.IO) {
            val result = fetchData()
            withContext(Dispatchers.Main) {
                showThreadName("1")
                mTvContent.text = result
            }
        }
        showThreadName("2")
    }

    /**
     * 协程 结合LiveData的使用
     * implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha02'
     */
    private fun useWithLiveData() {
        user.observe(this) {
            Log.e("TAG","userName${it.name}")
            mTvContent.text = it.name
        }
    }

    val user: LiveData<User> = liveData {
        emit(User("张三"))
        try {
            delay(5000)
            emit(User("李四"))
        } catch (ioException: Exception) {
            emit(User("王五"))
        }
    }
    data class User(val name: String = "sfdsfs")

    private suspend fun loadUser(): User {
        delay(3000)
        return User("王大锤")
    }



    //关键词 suspend
    private suspend fun fetchData(): String {
        Log.e("TAG", "fetchData--111" + Thread.currentThread().name)
        delay(3000) // delaying for 2 seconds to keep JVM alive
        Log.e("TAG", "fetchData--222" + Thread.currentThread().name)
        return "content111"
    }

    //关键词 suspend
    private suspend fun fetchData2(): String {
        Log.e("TAG", "fetchData2--111" + Thread.currentThread().name)
        delay(5000) // delaying for 2 seconds to keep JVM alive
        Log.e("TAG", "fetchData2--222" + Thread.currentThread().name)
        return "content22"
    }

    private fun showThreadName(tagIndex: String) {
        Log.e("TAG", tagIndex + Thread.currentThread().name)
    }

}

class MyViewModel : ViewModel() {
    override fun onCleared() {
        super.onCleared()
        Log.e("TAG,", "MyViewModel is Cleared")
    }
}

